package com.atguigu.gmall.search.service.impl;

import com.atguigu.gmall.product.search.Goods;
import com.atguigu.gmall.product.search.vo.SearchParamVo;
import com.atguigu.gmall.product.search.vo.SearchRespVo;
import com.atguigu.gmall.search.repo.GoodsRepository;
import com.atguigu.gmall.search.service.SearchService;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.HighlightQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    GoodsRepository goodsRepository;

    @Autowired
    ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Override
    public SearchRespVo search(SearchParamVo paramVo) {
        // 1. 获取检索时的Query条件
        Query query = getQuery(paramVo);
        SearchHits<Goods> result = elasticsearchRestTemplate.search(query, Goods.class, IndexCoordinates.of("goods"));

        // 2. 根据返回结果，构造前段需要的内容
        SearchRespVo searchRespVo = buildSearchResp(result, paramVo);

        return searchRespVo;
    }

    private SearchRespVo buildSearchResp(SearchHits<Goods> result, SearchParamVo paramVo) {
        SearchRespVo searchRespVo = new SearchRespVo();
        // 1. 检索参数
        searchRespVo.setSearchParam(paramVo);

        // 2. 品牌面包屑
        if (!StringUtils.isEmpty(paramVo.getTrademark())) {
            searchRespVo.setTrademarkParam("品牌：" + paramVo.getTrademark().split(":")[1]);
        }

        // 3. 属性面包屑
        if (!StringUtils.isEmpty(paramVo.getProps()) && paramVo.getProps().length > 0) {
            List<SearchRespVo.Props> collect = Arrays.stream(paramVo.getProps()).map(item -> {
                String[] split = item.split(":");
                SearchRespVo.Props props = new SearchRespVo.Props();
                props.setAttrId(Long.valueOf(split[0]));
                props.setAttrName(split[2]);
                props.setAttrValue(split[1]);
                return props;
            }).collect(Collectors.toList());
            searchRespVo.setPropsParamList(collect);
        }

        Aggregations aggregations = result.getAggregations();
        ParsedLongTerms tmIdAgg = aggregations.get("tmIdAgg");

        List<SearchRespVo.Trademark> trademarkList = tmIdAgg.getBuckets().stream().map(bucket -> {
            SearchRespVo.Trademark trademark = new SearchRespVo.Trademark();
            // id这里
            trademark.setTmId(bucket.getKeyAsNumber().longValue());

            // 品牌名
            ParsedStringTerms tmNameBucket = bucket.getAggregations().get("tmNameAgg");
            trademark.setTmName(tmNameBucket.getBuckets().get(0).getKeyAsString());


            // 品牌Url
            ParsedStringTerms tmLogoUrlBucket = bucket.getAggregations().get("tmLogoAgg");
            trademark.setTmLogoUrl(tmLogoUrlBucket.getBuckets().get(0).getKeyAsString());

            return trademark;
        }).collect(Collectors.toList());

        searchRespVo.setTrademarkList(trademarkList);


/*
//        ParsedNested nested = result.getAggregations().get("attrs");
//        ParsedLongTerms attrIdAgg = nested.getAggregations().get("attrId");

//        attrIdAgg.

//        ParsedLongTerms attrId = attrs.getAggregations().get("attrId");
//        attrId.getBuckets();

//        attrVos.setAttrId((Long) attrId.getBucketByKey("attrId").getKeyAsNumber());
//        attrVos.setAttrName(attrId.getBucketByKey("attrNameAge").getKeyAsString());*/
        ParsedNested attrsAgg = result.getAggregations().get("attrs");
        ParsedLongTerms attrIdAgg = attrsAgg.getAggregations().get("attrId");
        List<SearchRespVo.Attrs> attrsList = attrIdAgg.getBuckets().stream().map(bucket -> {
            SearchRespVo.Attrs attrs = new SearchRespVo.Attrs();

            // 存入Id
            attrs.setAttrId(bucket.getKeyAsNumber().longValue());

            // 存入属性名
            ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attrNameAge");
            attrs.setAttrName(attrNameAgg.getBuckets().get(0).getKeyAsString());

            // 存入属性值
            ParsedStringTerms attrValues = bucket.getAggregations().get("attrs.attrValue");
            List<String> attrValue = attrValues.getBuckets()
                    .stream()
                    .map(bucket1 -> bucket1.getKeyAsString())
                    .collect(Collectors.toList());
            attrs.setAttrValueList(attrValue);

            return attrs;
        }).collect(Collectors.toList());

        searchRespVo.setAttrsList(attrsList);

        // ： 6. URL参数
        String url = buildUrlString(paramVo);
        searchRespVo.setUrlParam(url);

        // 7. 排序信息
        SearchRespVo.OrderMap orderMap = new SearchRespVo.OrderMap();
        if (!StringUtils.isEmpty(paramVo.getOrder())) {
            String[] split = paramVo.getOrder().split(":");
            orderMap.setSort(split[1]);
            orderMap.setType(split[0]);
            searchRespVo.setOrderMap(orderMap);
        } else {
            searchRespVo.setOrderMap(orderMap);
        }

        // 8. 商品信息
        for (SearchHit<Goods> searchHit : result.getSearchHits()) {
            Goods content = searchHit.getContent();
        }
        List<Goods> collect = result.getSearchHits().stream().map(item -> {
                    Goods content = item.getContent();
                    if (!StringUtils.isEmpty(paramVo.getKeyword())){
                        content.setTitle(item.getHighlightField("title").get(0));
                    }
                    return content;
                })
                .collect(Collectors.toList());
        searchRespVo.setGoodsList(collect);

        //  9. 页码信息
        searchRespVo.setPageNo(paramVo.getPageNo());

        // 10. 总记页码
        searchRespVo.setTotalPages(result.getTotalHits() % 10 == 0 ? (int) result.getTotalHits() / 10 : (int) ((result.getTotalHits() / 10) + 1));
        return searchRespVo;
    }

    private static String buildUrlString(SearchParamVo paramVo) {
        StringBuilder urlParam = new StringBuilder("list.html?");
        //分类参数
        if (paramVo.getCategory1Id() != null) {
            urlParam.append("&category1Id=" + paramVo.getCategory1Id());
        }
        if (paramVo.getCategory2Id() != null) {
            urlParam.append("&category2Id=" + paramVo.getCategory2Id());
        }
        if (paramVo.getCategory3Id() != null) {
            urlParam.append("&category3Id=" + paramVo.getCategory3Id());
        }

        //keyword
        if (!StringUtils.isEmpty(paramVo.getKeyword())) {
            urlParam.append("&keyword=" + paramVo.getKeyword());
        }

        //品牌
        if (!StringUtils.isEmpty(paramVo.getTrademark())) {
            urlParam.append("&trademark=" + paramVo.getTrademark());
        }

        //属性
        if (paramVo.getProps() != null && paramVo.getProps().length > 0) {
            Arrays.stream(paramVo.getProps()).forEach(item -> {
                urlParam.append("&props=" + item);
            });
        }
        return urlParam.toString();
    }

    public static Query getQuery(SearchParamVo paramVo) {

        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        // 1.1. getCategory1Id
        if (paramVo.getCategory1Id() != null) {
            boolQuery.must(QueryBuilders.termQuery("category1Id", paramVo.getCategory1Id()));
        }

        // 1.2. getCategory2Id
        if (paramVo.getCategory2Id() != null) {
            boolQuery.must(QueryBuilders.termQuery("category2Id", paramVo.getCategory2Id()));
        }

        // 1.3. getCategory3Id
        if (paramVo.getCategory3Id() != null) {
            boolQuery.must(QueryBuilders.termQuery("category3Id", paramVo.getCategory3Id()));
        }

        // 1.4. 关键字查询
        if (paramVo.getKeyword() != null) {
            boolQuery.must(QueryBuilders.matchQuery("title", paramVo.getKeyword()));
        }

        // 1.5. 品牌查询
        if (!StringUtils.isEmpty(paramVo.getTrademark())) {
            String[] tradeMark = paramVo.getTrademark().split(":");
            boolQuery.must(QueryBuilders.termQuery("tmId", tradeMark[0]));
        }

        // 1.6. 属性查询
        if (paramVo.getProps() != null && paramVo.getProps().length > 0) {
            Arrays.stream(paramVo.getProps()).forEach(item -> {
                String[] split = item.split(":");
                BoolQueryBuilder builder = QueryBuilders.boolQuery();

                builder.must(QueryBuilders.termQuery("attrs.attrId", split[0]));
                builder.must(QueryBuilders.termQuery("attrs.attrValue", split[1]));
                NestedQueryBuilder query = QueryBuilders.nestedQuery("attrs", builder, ScoreMode.None);
                boolQuery.must(query);
            });
        }
        //==================查询结束


        NativeSearchQuery query = new NativeSearchQuery(boolQuery);

        //==================排序开始
        Sort sort = null;
        if (!StringUtils.isEmpty(paramVo.getOrder())) {
            String[] split = paramVo.getOrder().split(":");
            Sort.Direction direction = "asc".equals(split[1]) ? Sort.Direction.ASC : Sort.Direction.DESC;

            switch (split[0]) {
                // 0默认排序
                case "1":
                    sort = Sort.by(direction, "hotScore");
                    break;
                case "2":
                    sort = Sort.by(direction, "price");
                    break;
                default:
                    sort = Sort.by(Sort.Direction.DESC, "hotScore");
            }
            sort = Sort.by(Sort.Direction.DESC, "hotScore");
            query.addSort(sort);
        } else {
            sort = Sort.by(Sort.Direction.DESC, "hotScore");
            query.addSort(sort);
        }
        //==================排序结束
        //==================分页开始

        Pageable pagerAble = PageRequest.of(paramVo.getPageNo() - 1, 10);
        query.setPageable(pagerAble);

        //==================分页结束
        //==================聚合分析开始

        // 1. 品牌信息
        TermsAggregationBuilder tmIdAgg = AggregationBuilders
                .terms("tmIdAgg")
                .field("tmId")
                .size(200);
        tmIdAgg.subAggregation(AggregationBuilders.terms("tmNameAgg").field("tmName").size(1));
        tmIdAgg.subAggregation(AggregationBuilders.terms("tmLogoAgg").field("tmLogoUrl").size(1));
        query.addAggregation(tmIdAgg);


        // 2. 属性信息
        NestedAggregationBuilder attrAgg = AggregationBuilders.nested("attrs", "attrs");

        TermsAggregationBuilder attrIdAgg = AggregationBuilders
                .terms("attrId")
                .field("attrs.attrId");

        attrIdAgg.subAggregation(AggregationBuilders
                .terms("attrNameAge")
                .field("attrs.attrName")
                .size(200));

        attrIdAgg.subAggregation(
                AggregationBuilders
                        .terms("attrs.attrValue")
                         .field("attrs.attrValue")
                        .size(200)
        );

        NestedAggregationBuilder nestedAggregationBuilder = attrAgg.subAggregation(attrIdAgg);
//
//        NestedAggregationBuilder attrId = attrAgg.subAggregation(
//                AggregationBuilders.terms("attrId")
//                        .field("attrs.attrId")
//        );
//        attrId.subAggregation(AggregationBuilders.terms("attrNameAge")
//                .field("attrs.attrName").size(200));
//
//        attrId.subAggregation(
//                AggregationBuilders.terms("attrValueAge")
//                        .field("attrs.attrValue")
//                        .size(200)
//        );

        query.addAggregation(attrAgg);
        //==================聚合分析结束

        //==================设置高亮显示
        if (!StringUtils.isEmpty(paramVo.getKeyword())){
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder
                    .field("title")
                    .preTags("<span style='color:red'>")
                    .postTags("</span>");
            HighlightQuery highlightQuery = new HighlightQuery(highlightBuilder);
            query.setHighlightQuery(highlightQuery);
        }
        //==================高亮显示设置结束




        return query;
    }

    @Override
    public void up(Goods goods) {
        goodsRepository.save(goods);
    }

    @Override
    public void down(Long skuId) {
        goodsRepository.deleteById(skuId);
    }

    @Override
    public void updateHotScore(Long skuId, Long score) {
        Document document = Document.create();
        document.put("hotScore",score);

        UpdateQuery build = UpdateQuery.builder("" + skuId)
                .withDocAsUpsert(true)
                .withDocument(document)
                .build();
        elasticsearchRestTemplate.update(build,IndexCoordinates.of("goods"));
    }
}
